# 定义注册的序列化器
import re

from django_redis import get_redis_connection
from rest_framework import serializers
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from rest_framework.serializers import ModelSerializer
from rest_framework.views import APIView

from goods.models import SKU
from users import constants
from users.models import User, Address
from users.utils import generate_save_user_token_url
from celery_tasks.email.tasks import send_verify_email


class CreateUserSerializer(ModelSerializer):
    """创建序列化器"""

    password2 = serializers.CharField(label="确认密码", write_only=True)
    sms_code = serializers.CharField(label="短信验证码", write_only=True)
    allow = serializers.CharField(label="同意协议", write_only=True)
    token = serializers.CharField(label="token", read_only=True)

    class Meta:
        model = User
        fields = ["token", "id", "username", "password", "password2", "sms_code", "mobile", "allow"]
        # 验证用户名和密码
        extra_kwargs = {
            "username": {
                "min_length": 5,
                "max_length": 20,
                "error_messages": {
                    "min_length": "仅允许5-20个字符的用户名",
                    "max_length": "仅允许5-20个字符的用户名",
                }
            },
            "password": {
                "min_length": 8,
                "max_length": 20,
                "error_messages": {
                    "min_length": "仅允许8-20个字符的用户名",
                    "max_length": "仅允许8-20个字符的用户名",
                }
            },
        }

    def validate_mobile(self, value):
        """验证手机号码"""
        if not re.match(r"^1[3-9]\d{9}$", value):
            raise serializers.ValidationError("手机号码输入的格式错误")
        return value

    def validate_allow(self, value):
        """判断是否同意规则"""
        if value != "true":
            raise serializers.ValidationError("请同意用户协议！")
        return value

    def validate(self, attrs):
        """验证两次密码是不是一致"""
        if attrs["password"] != attrs["password2"]:
            raise serializers.ValidationError("输入的密码不一致")

        """验证短信验证码 1 连接redis 2 判断数据是不是存在　3 若存在，比较值是不是相等"""
        redis_conn = get_redis_connection("verify")
        # print(attrs)
        mobile = attrs["mobile"]
        # print(mobile)
        real_sms_code = redis_conn.get("sms_%s" % mobile)
        # print(real_sms_code)
        if real_sms_code is None:
            raise serializers.ValidationError("无效的短信验证码")
        if attrs["sms_code"] != real_sms_code.decode():
            raise serializers.ValidationError("短信验证码错误")
        return attrs

    def create(self, validated_data):
        """创建用户"""
        #  先移除部分数据
        del validated_data["password2"]
        del validated_data["sms_code"]
        del validated_data["allow"]
        user = super().create(validated_data)

        #  给密码加密　并且更新数据库
        user.set_password(validated_data["password"])
        user.save()

        # 注册成功后，需要ｊｗｔ保存相应的登陆状态
        from rest_framework_jwt.settings import api_settings
        # 获取 生成载荷的函数
        jmt_payload_handle = api_settings.JWL_PAYLOAD_HANDLE
        # 获取 生成token的函数
        jwt_encode_handle = api_settings.JWL_ENCODE_HANDLE

        payload = jmt_payload_handle(user)
        token = jwt_encode_handle(payload)

        user.token = token
        return user


class EmailSerializer(serializers.ModelSerializer):
    """
    邮箱序列化器
    """

    class Meta:
        model = User
        fields = ['id', 'email']
        extra_kwargs = {
            'email': {
                'required': True
            }
        }

    def update(self, instance, validated_data):
        instance.email = validated_data['email']
        instance.save()
        # 生成验证邮箱的链接[instance 就是当前已经登陆的用户]
        verify_url = generate_save_user_token_url(instance)

        # 异步发送邮件
        send_verify_email.delay(instance.email, verify_url)
        return instance


# 创建地址的序列化器
class UserAddressSerializer(serializers.ModelSerializer):
    # 声明字段
    # 用于显示给客户端浏览器的地址信息[增加__str__方法的关联]
    # StringRelatedField　　用unicode方法表示只读关系,
    # PrimaryKeyRelatedField　　用主键表示关系　
    # SlugRelatedField　　选取关系对象中任意一个字段（唯一标识）表示关系
    # tracks = serializers.SlugRelatedField(
    #     many=True,
    #     read_only=True,
    #     slug_field='title'
    # )
    # 　Nested relationships　　嵌套定义　　ｅｇ　tracks = TrackSerializer(many=True, read_only=True)
    # 以下三个参数，前段没有传递相应的数据，而是传递的值为相应的ＩＤ，　
    # 所以还需要定义其他三个必须的字段，方便反序列化
    # 所以使用的方法是使用StringRelatedField 参见https://www.jianshu.com/p/bb61717219f9
    # read_only=True 表示序列化后数据不会传入前段，但是反序列化后的结果可以传入数据库
    province = serializers.StringRelatedField(read_only=True)
    city = serializers.StringRelatedField(read_only=True)
    district = serializers.StringRelatedField(read_only=True)

    # 设置保存到数据表的id信息[取消关联]
    province_id = serializers.IntegerField(label='省ID', required=True)
    city_id = serializers.IntegerField(label='市ID', required=True)
    district_id = serializers.IntegerField(label='区ID', required=True)

    class Meta:
        model = Address
        # 表示排除掉列表中设置的字段，剩下的字段都要在序列化器使用
        exclude = ['user', 'is_deleted', 'create_time', 'update_time']

    # 校验数据
    def validate_mobile(self, value):
        """
        验证手机号
        """
        if not re.match(r'^1[3-9]\d{9}$', value):
            raise serializers.ValidationError('手机号格式错误')
        return value

    def create(self, validated_data):
        # 获取当前登陆用户
        # self.context  就是实例化序列化器时的第三个参数，可以允许视图那边传递数据到序列化器中
        # self.context　提供了三个参数： view视图对象, request请求对象, format参数的格式
        # 在GenericAPIView里面的get_serializer方法中默认帮我们把视图对象传递到了序列化器中了
        # 当前已经登陆的用户保存到过滤后的数据中
        # user可以通过request获得，可以无需在序列化器中定义相应的值，并且指定write_only=True
        # 可以直接在create中增加验证的数据user,最终将数据保存模型中
        validated_data["user"] = self.context["request"].user
        # 调用原来drf的序列化器的保存方法
        return super().create(validated_data)


class AddressTitleSerializer(serializers.ModelSerializer):
    """
    地址标题
    """

    class Meta:
        model = Address
        fields = ('title',)


class AddUserBrowsingHistorySerializer(serializers.Serializer):
    """
    浏览历史的序列化器，　只有一个字段，为商品的id号码
    """
    sku_id = serializers.IntegerField(label="商品SKU编号", min_value=1)

    def validated_sku_id(self, value):
        """
        校验sku_id是不是存在
        """
        try:
            SKU.objects.get(id=value)
        except SKU.DoesNotExist:
            raise serializers.ValidationError("商品不存在")
        return value

    def create(self, validated_data):
        """
        获取当前用户的id, context是视图提供的内容，父类已经提供封装了相应的字段
        redis中保存历史记录的数据类型格式
        例如，本次用户访问的sku是2,
        user_id: [
           7,6,1,4,2
        ]
        """
        user_id = self.context["request"].user.id
        sku_id = validated_data["sku_id"]
        # 把sku_id保存redis的list类型数据中
        redis_conn = get_redis_connection("history")
        pl = redis_conn.pipeline()
        # 1 首先删除redis 中已经存在的数据
        pl.lrem("history_%s" % user_id, 0, sku_id)
        # 2 然后添加sku_id 至数据库
        pl.lpush("history_%s" % user_id, sku_id)
        # 3 如果数据超过了长度，删除相应得到数据，用到的方法是ltrim
        pl.ltrim("history_%s" % user_id, 0, constants.USER_BROWSING_HISTORY_COUNTS_LIMIT - 1)
        # 4 执行所有的pipeline
        pl.execute()
        return validated_data
